// AccountOwnership.aj
//
// Informatics 102 Spring 2012
// Code Example
//
// This aspect implements the notion of account ownership, taking the
// Account class -- which has no support for ownership -- and makes
// the appropriate changes to it.  New in this example are inter-type
// declarations for methods and the "declare parents" declaration,
// which are described where used below.

public aspect AccountOwnership
{
	// This declaration says "Account needs a new parent (i.e., a class
	// it extends or an interface that it implements."  In this case,
	// part of making accounts be ownable is to plug them into our
	// grander notion of things that can be owned, which is embodied by
	// our Ownable interface.
	declare parents: Account implements Ownable;


	// Here, we're adding an ownerName field to the Account class.  As
	// we've seen previously, by marking this field private, we're
	// making it private to the aspect (i.e., all Accounts have this
	// field, but even the Account class doesn't know about it; only
	// the aspect does).
	private String Account.ownerName;


	// This is how you add a method to an existing class using an
	// inter-type declaration.  It's as simple as declaring a normal
	// method, except that we're including a class name and a dot in
	// front of its name.
	public String Account.getOwnerName()
	{
		return ownerName;
	}
	
	
	public void Account.setOwnerName(String newOwnerName)
	{
		ownerName = newOwnerName;
	}
	
	
	// Here, we'll do something we've done previously: recognize certain
	// methods that can be called on an Account, hoisting into a pointcut
	// parameter the Account the methods are called on.  Then we'll apply
	// before advice that will throw an exception if the current user
	// isn't the owner of the Account.
	pointcut sensitiveAccountMethods(Account account):
		(call (* Account.getBalance(..)) ||
		 call (* Account.deposit(..)) ||
		 call (* Account.withdraw(..)) ||
		 call (* Account.transferTo(..))) && target(account);


	before(Account account):
		sensitiveAccountMethods(account)
	{
		requireAccountOwnedByCurrentUser(account);
	}


	// The one case not handled above is the transferTo() method, which
	// can be problematic if either of the accounts (the one we're
	// transferring from or the one we're transferring to) are not
	// owned by the current user.
	pointcut methodsWithSensitiveAccountParameters(Account account):
		call(* Account.transferTo(Account, int)) && args(account, ..);


	before(Account account):
		methodsWithSensitiveAccountParameters(account)
	{
		requireAccountOwnedByCurrentUser(account);
	}


	private void requireAccountOwnedByCurrentUser(Account account)
	{
		if (!Login.getCurrentUser().equals(account.getOwnerName()))
		{
			throw new AccountAccessException();
		}
	}
}
